package com.jsbs.iam.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jsbs.iam.auth.constant.Constants;
import com.jsbs.iam.auth.constant.RedisConstants;
import com.jsbs.iam.auth.dto.IamAuthDto;
import com.jsbs.iam.auth.entity.IamRoleOrgFrameworkRelation;
import com.jsbs.iam.auth.entity.IamRoleUserRelation;
import com.jsbs.iam.auth.entity.IamUserGroupDynamicFilterRule;
import com.jsbs.iam.auth.feign.IamRoleManagerFeign;
import com.jsbs.iam.auth.mapper.*;
import com.jsbs.iam.auth.service.IamAuthService;
import com.jsbs.iam.auth.vo.ApplySystemVo;
import com.jsbs.iam.auth.vo.IamAuthButtonVo;
import com.jsbs.iam.auth.vo.IamAuthMenuVo;
import com.jsbs.iam.auth.vo.UserInfoVo;
import com.jsbs.iam.common.core.dto.Result;
import com.jsbs.iam.common.core.utils.Json;
import com.jsbs.iam.common.core.utils.StringUtils;
import com.jsbs.iam.common.redis.service.RedisUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author junwc
 * @className IamAuthServiceImpl
 * @date 2022/12/27 12:29
 * @description:
 */
@Service
public class IamAuthServiceImpl implements IamAuthService {

    @Autowired
    private IamRoleUserRelationMapper iamRoleUserRelationMapper;

    @Autowired
    private IamRoleOrgFrameworkRelationMapper iamRoleOrgFrameworkRelationMapper;

    @Autowired
    private IamRoleManagerFeign iamRoleManagerFeign;

    @Autowired
    private IamRoleMenuRelationMapper iamRoleMenuRelationMapper;

    @Autowired
    private IamRoleButtonRelationMapper iamRoleButtonRelationMapper;

    @Autowired
    private IamUserGroupUserRelationMapper iamUserGroupUserRelationMapper;

    @Autowired
    private IamUserGroupDynamicFilterRuleMapper iamUserGroupDynamicFilterRuleMapper;

    @Autowired
    private IamUserGroupMenuRelationMapper iamUserGroupMenuRelationMapper;

    @Autowired
    private IamUserGroupButtonRelationMapper iamUserGroupButtonRelationMapper;

    @Value("${ENCRYPT.KEY}")
    private String ENCRYPT_KEY;

    @Value("${_env.name}")
    private String envName;

    @Override
    public Result accessAuthCheck(IamAuthDto iamAuthDto) {
        if (StringUtils.isEmpty(iamAuthDto.getUserCode())) {
            return Result.failWithMsg("当前用户信息为空");
        }
        if (StringUtils.isEmpty(iamAuthDto.getServerName())) {
            return Result.failWithMsg("访问的域名为空");
        }
        if (StringUtils.isEmpty(iamAuthDto.getUrl())) {
            return Result.failWithMsg("访问的URL为空");
        }
        if (StringUtils.isEmpty(iamAuthDto.getCompanyCode())) {
            return Result.failWithMsg("归属租户为空");
        }
        // 根据请求域名获取应用编码
        Result applySystemResult = iamRoleManagerFeign.getApplySystem(iamAuthDto.getServerName());
        if (Objects.isNull(applySystemResult)) {
            return Result.failWithMsg("找不到应用信息");
        }
        ApplySystemVo applySystemVo = JSONObject.parseObject(Json.toJsonString(applySystemResult.getData()), ApplySystemVo.class);
        // 请求的应用编码
        iamAuthDto.setApplyCode(applySystemVo.getApplyCode());
        // 根据用户获取对应业务系统所有菜单/按钮权限列表
        Result authList = queryPermissonList(iamAuthDto);
        // 根据权限类型 获取对应菜单/按钮权限列表
        List<IamAuthMenuVo> authMenuVoList = JSONArray.parseArray(JSON.toJSONString(authList.getData()), IamAuthMenuVo.class);
        if (CollectionUtils.isEmpty(authMenuVoList)) {
            return Result.fail(Result.GO_ON, "鉴权失败");
        }
        // 获取所有的菜单、按钮URL
        List<String> urlList = getAuthUrlList(authMenuVoList);
        if (!urlList.contains(iamAuthDto.getUrl())) {
            return Result.fail(Result.GO_ON, "鉴权失败");
        }
        return Result.success("鉴权成功", null);
    }

    /**
     * 功能描述：获取所有的菜单按钮URL集合
     * @param authMenuVoList
     * @return
     */
    private List<String> getAuthUrlList(List<IamAuthMenuVo> authMenuVoList){
        List<String> urlList = new ArrayList<>();
        for (IamAuthMenuVo authMenuVo : authMenuVoList) {
            if (StringUtils.isNotEmpty(authMenuVo.getMenuUrl())) {
                urlList.addAll(Arrays.asList(authMenuVo.getMenuUrl().split(",")));
            }

            if (CollectionUtils.isNotEmpty(authMenuVo.getButtonList())) {
                List<String> buttonList = authMenuVo.getButtonList().stream().map(item -> item.getButtonUrl()).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(buttonList)) {
                    buttonList.forEach(item -> {
                        // 按钮URL
                        urlList.addAll(Arrays.asList(item.split(",")));
                    });
                }
            }
            if (CollectionUtils.isNotEmpty(authMenuVo.getChildren())) {
                urlList.addAll(getAuthUrlList(authMenuVo.getChildren()));
            }
        }
        return urlList;
    }

    @Override
    public Result queryPermissonList(IamAuthDto iamAuthDto) {
        if (StringUtils.isEmpty(iamAuthDto.getUserCode())) {
            return Result.failWithMsg("用户编码为空");
        }
        // 管理员用户权限
        if (Constants.USER_TYPE_ADMIN.equals(iamAuthDto.getUserType())) {
            if (StringUtils.isEmpty(iamAuthDto.getAdminRoleCode())) {
                return Result.failWithMsg("管理员角色为空");
            }
            return this.queryAdminPermissonList(iamAuthDto.getUserCode(), iamAuthDto.getAdminRoleCode());
        }
        // 普通用户权限
        if (StringUtils.isEmpty(iamAuthDto.getCompanyCode())) {
            return Result.failWithMsg("归属租户为空");
        }
        if (StringUtils.isEmpty(iamAuthDto.getApplyCode())) {
            return Result.failWithMsg("应用编码为空");
        }

        // 先从缓存取，取不到查库，并重新入缓存
        List<IamAuthMenuVo> menuVoList = RedisUtils.getObjectList(envName + RedisConstants.AUTH_MENU_BUTTON_INFO + iamAuthDto.getUserCode() + iamAuthDto.getApplyCode(), IamAuthMenuVo.class);
        // 缓存存在直接返回
        if (CollectionUtils.isNotEmpty(menuVoList)) {
            return Result.success(menuVoList);
        }

        // 根据用户编码+应用编码获取权限列表
        // 1.根据用户编码+归属租户 获取用户直接关联的角色
        IamRoleUserRelation roleUser = iamRoleUserRelationMapper.queryRoleByUser(iamAuthDto.getUserCode(), iamAuthDto.getCompanyCode());
        // 2.根据用户编码+归属租户 获取用户对应组织关联的角色
        // 根据用户编码获取用户详细信息
        Result result = iamRoleManagerFeign.queryByUserCode(iamAuthDto.getCompanyCode(), iamAuthDto.getUserCode());
        UserInfoVo userInfoVo = JSON.parseObject(JSON.toJSONString(result.getData()), UserInfoVo.class);
        // 组织集合
        List<String> orgInfoList = new ArrayList<>();
        // 岗位集合
        /*List<String> postIdList = new ArrayList<>();*/
        orgInfoList.addAll(Arrays.asList(userInfoVo.getOrgCode().split(",")));
        // 根据组织+岗位获取对应角色信息
        List<IamRoleOrgFrameworkRelation> roleOrgList = iamRoleOrgFrameworkRelationMapper.queryRoleByOrg(iamAuthDto.getCompanyCode(), orgInfoList, orgInfoList);
        // 角色集合
        List<String> roleList = new ArrayList<>();
        if (Objects.nonNull(roleUser)) {
            roleList.add(roleUser.getRoleCode());
        }
        if (CollectionUtils.isNotEmpty(roleOrgList)) {
            roleList.addAll(roleOrgList.stream().map(item -> item.getRoleCode()).collect(Collectors.toList()));
        }
        // 当前用户权限列表
        List<IamAuthMenuVo> authMenuVoList = new ArrayList<>();
        // 根据角色获取菜单权限集合
        List<String> menuCodeList = iamRoleMenuRelationMapper.queryByRole(iamAuthDto.getApplyCode(), roleList);
        // 根据角色获取按钮菜单集合
        List<String> buttonCodeList = iamRoleButtonRelationMapper.queryByRole(iamAuthDto.getApplyCode(), roleList);

        // 3.根据用户编码+归属租户 获取用户所属的静态用户组
        List<String> userGroupCodeList = iamUserGroupUserRelationMapper.queryByUserCode(iamAuthDto.getUserCode(), iamAuthDto.getCompanyCode());
        // 4.根据用户编码+归属租户 获取用户所属的动态用户组
        List<IamUserGroupDynamicFilterRule> userGroupDynamicFilterRules = iamUserGroupDynamicFilterRuleMapper.queryForRule(iamAuthDto.getCompanyCode());
        // 根据用户组编码分组
        Map<String, List<IamUserGroupDynamicFilterRule>> userGroupRuleMap = userGroupDynamicFilterRules.stream().collect(Collectors.groupingBy(IamUserGroupDynamicFilterRule::getUserGroupCode));
        // 将用户信息封装成map，下划线
        Map<String, Object> userToMap = convernetIntoMapValue(userInfoVo);
        // 封装扩展字段
        userInfoVo.getExtendData().forEach(item -> {
            userToMap.put(MapUtils.getString(item, Constants.FIELD_ID), MapUtils.getObject(item, Constants.FIELD_DATA));
        });
        userGroupRuleMap.forEach((k, v) -> {
            // 验证是否满足动态用户组的所有规则条件
            // 不满足跳过
            if (!checkForCondition(userToMap, v)) {
                return;
            }
            // 满足所有条件，则入集合
            userGroupCodeList.add(k);
        });
        // 根据满足条件的用户组 获取对应的所有权限数据
        List<String> userMenuCodeList = iamUserGroupMenuRelationMapper.queryByUserCode(iamAuthDto.getApplyCode(), userGroupCodeList);
        List<String> userButtonCodeList = iamUserGroupButtonRelationMapper.queryByUserCode(iamAuthDto.getApplyCode(), userGroupCodeList);
        if (CollectionUtils.isNotEmpty(userMenuCodeList)) {
            menuCodeList.addAll(userMenuCodeList);
            // 菜单编码去重
            menuCodeList = menuCodeList.stream().distinct().collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(userButtonCodeList)) {
            buttonCodeList.addAll(userButtonCodeList);
            // 按钮去重
            buttonCodeList = buttonCodeList.stream().distinct().collect(Collectors.toList());
        }

        // 根据菜单编码获取菜单权限信息
        Result menuInfoResult = iamRoleManagerFeign.getMenuInfoList(menuCodeList);
        List<IamAuthMenuVo> roleMenuList = JSONArray.parseArray(Json.toJsonString(menuInfoResult.getData()), IamAuthMenuVo.class);
        // 根据按钮编码获取按钮权限信息
        Result buttonInfoResult = iamRoleManagerFeign.getButtonInfoList(buttonCodeList);
        List<IamAuthButtonVo> roleButtonList = JSONArray.parseArray(Json.toJsonString(buttonInfoResult.getData()), IamAuthButtonVo.class);
        // 根据手机号+员工编码拼接BI报表URL
        String url = dealForMenuUrl(userInfoVo.getPhone(), userInfoVo.getEmployeeCode());
        if (CollectionUtils.isNotEmpty(roleMenuList)) {
            roleMenuList.forEach(item -> {
                // 获取按钮列表
                item.setButtonList(roleButtonList.stream().filter(info -> info.getMenuCode().equals(item.getMenuCode())).collect(Collectors.toList()));
                // 处理urlFlag=BI的报表链接
                if (Constants.URL_FLAG_BI.equals(item.getUrlFlag())) {
                    item.setMenuUrl(item.getMenuUrl() + url);
                }
            });
            // 获取所有一级菜单，根据菜单排序号排序
            authMenuVoList = roleMenuList.stream().filter(item -> "1".equalsIgnoreCase(item.getMenuLevel())).sorted(Comparator.comparing(IamAuthMenuVo::getMenuSort)).collect(Collectors.toList());
            // 递归封装子级菜单
            dealForIamMenu(roleMenuList, authMenuVoList);
        }
        if (CollectionUtils.isNotEmpty(authMenuVoList)) {
            // 权限结果不为空，则入缓存
            RedisUtils.setObjectList(envName + RedisConstants.AUTH_MENU_BUTTON_INFO + iamAuthDto.getUserCode() + iamAuthDto.getApplyCode(), authMenuVoList, 12, TimeUnit.HOURS);
        }
        return Result.success(authMenuVoList);
    }

    /**
     * 功能描述：递归封装子级菜单
     * @param roleMenuList
     * @param authMenuVoList
     */
    private void dealForIamMenu(List<IamAuthMenuVo> roleMenuList, List<IamAuthMenuVo> authMenuVoList){
        // 循环当前层级菜单
        authMenuVoList.forEach(item -> {
            List<IamAuthMenuVo> menuVoList = new ArrayList<>();
            // 循环所有菜单，匹配当前菜单的所有子级菜单
            for (IamAuthMenuVo authMenuVo : roleMenuList) {
                if (item.getMenuCode().equalsIgnoreCase(authMenuVo.getMenuSuperiorCode())) {
                    // 入集合
                    menuVoList.add(authMenuVo);
                }
                // 递归封装当前菜单的子级菜单的所有子级
                dealForIamMenu(roleMenuList, menuVoList);
                // 每次递归结束都拍一次序
                menuVoList.sort(Comparator.comparing(IamAuthMenuVo::getMenuSort));
            }
            // 封装当前菜单的所有子级菜单集合
            item.setChildren(menuVoList);
        });
    }

    /**
     * 功能描述：将实体类转成map
     * @param userInfoVo
     * @return
     */
    private static Map<String, Object> convernetIntoMapValue(UserInfoVo userInfoVo){
        Map<String, Object> result = new HashMap<>();
        try {
            Field[] fields = userInfoVo.getClass().getDeclaredFields();
            String separator = "_";
            for (Field field : fields) {
                String name = field.getName();
                // 排除扩展字段集合
                if ("extendData".equalsIgnoreCase(name)) {
                    continue;
                }
                name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写，方便构造get，set方法
                Method m = userInfoVo.getClass().getMethod("get" + name);
                result.put(field.getName().replaceAll("([a-z])([A-Z])", "$1"+separator+"$2").toLowerCase(), m.invoke(userInfoVo));
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 功能描述：根据条件判断是否满足规则
     * @param userToMap
     * @param ruleList
     * @return
     */
    private boolean checkForCondition(Map<String, Object> userToMap, List<IamUserGroupDynamicFilterRule> ruleList) {
        try {
            boolean ruleCondition = true;
            for (IamUserGroupDynamicFilterRule rule : ruleList) {
                switch (rule.getRuleCondition()) {
                    // 规则：>
                    case ">":
                        if (!(MapUtils.getIntValue(userToMap, rule.getRuleField()) > Integer.valueOf(rule.getRuleValue()))) {
                            return false;
                        }
                    case ">=":
                        if (!(MapUtils.getIntValue(userToMap, rule.getRuleField()) >= Integer.valueOf(rule.getRuleValue()))) {
                            return false;
                        }
                    case "=":
                        if (!(MapUtils.getIntValue(userToMap, rule.getRuleField()) == Integer.valueOf(rule.getRuleValue()))) {
                            return false;
                        }
                    case "<":
                        if (!(MapUtils.getIntValue(userToMap, rule.getRuleField()) < Integer.valueOf(rule.getRuleValue()))) {
                            return false;
                        }
                    case "<=":
                        if (!(MapUtils.getIntValue(userToMap, rule.getRuleField()) <= Integer.valueOf(rule.getRuleValue()))) {
                            return false;
                        }
                    case "包含":
                        if (!(MapUtils.getString(userToMap, rule.getRuleField()).contains(rule.getRuleValue()))) {
                            return false;
                        }
                    case "不包含":
                        if (MapUtils.getString(userToMap, rule.getRuleField()).contains(rule.getRuleValue())) {
                            return false;
                        }
                    default:
                        ruleCondition = true;
                }
            }
            // 默认返回true
            return ruleCondition;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取加密URL
     * @param phone
     * @param empCode
     * @return
     */
    private String dealForMenuUrl(String phone, String empCode){
        String url = "?";
        if (StringUtils.isNotEmpty(phone)) {
            url.concat("phone=").concat(iamRoleUserRelationMapper.selectPhoneEncrypt(phone, ENCRYPT_KEY));
        }
        if (StringUtils.isNotEmpty(empCode)) {
            url.concat("&employee_code=").concat(iamRoleUserRelationMapper.selectPhoneEncrypt(empCode, ENCRYPT_KEY));
        }
        return url;
    }

    public Result queryAdminPermissonList(String userCode, String adminRoleCode){
        Result permissonResult = iamRoleManagerFeign.queryAdminPermisson(userCode, adminRoleCode);
        if (Objects.isNull(permissonResult)) {
            return Result.failWithMsg("权限列表为空");
        }
        return permissonResult;
    }
}
